package edu.uic.cs.cs582.q_wordnet.process.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.collections.primitives.ArrayIntList;
import org.apache.log4j.Logger;

import edu.uic.cs.cs582.data.Component;
import edu.uic.cs.cs582.data.Polarity;
import edu.uic.cs.cs582.data.Synset;
import edu.uic.cs.cs582.data.SynsetSet;
import edu.uic.cs.cs582.data.Word;
import edu.uic.cs.cs582.misc.LogHelper;
import edu.uic.cs.cs582.process.impl.IntArrayComparator;
import edu.uic.cs.cs582.process.impl.MajoritySynsetsCnfGeneratorImpl;
import edu.uic.cs.cs582.q_wordnet.data.SynsetsPolarityStore;

class QWordNetMajoritySynsetsCnfGeneratorImpl extends
		MajoritySynsetsCnfGeneratorImpl
{
	private static final Logger LOGGER = LogHelper
			.getLogger(QWordNetMajoritySynsetsCnfGeneratorImpl.class);

	public QWordNetMajoritySynsetsCnfGeneratorImpl(Component component)
	{
		super(component);
	}

	@Override
	protected void checkGeneratedCnfs(Word word, Polarity polarity,
			Collection<int[]> wordExpressionCnfs)
	{
		// Do nothing... 
	}

	@Override
	protected List<int[]> convertDnfToCnf(List<int[]> dnfs)
	{
		if (dnfs == null)
		{
			if (LOGGER.isDebugEnabled())
			{
				LOGGER.debug("Input DNFs is null, return empty set to represent the converted CNFs. ");
			}

			return Collections.emptyList();
		}

		return super.convertDnfToCnf(dnfs);
	}

	@Override
	protected List<int[]> generatePositiveWordExpression(Word word)
	{
		return generateWordExpression(word.getMajoritySynsets(),
				Polarity.POSITIVE, word.getWord());
	}

	@Override
	protected List<int[]> generateNegativeWordExpression(Word word)
	{
		return generateWordExpression(word.getMajoritySynsets(),
				Polarity.NEGATIVE, word.getWord());
	}

	/**
	 * (NOT v11 OR NOT v21) AND (NOT v11 OR NOT v31) AND (NOT v21 OR NOT v31 OR NOT v41)
	 * AND
	 * (NOT v12 OR NOT v22) AND (NOT v12 OR NOT v32) AND (NOT v22 OR NOT v32 OR NOT v42)
	 */
	@Override
	protected Set<int[]> generateNeutralWordExpression(Word word)
	{

		Set<int[]> notPositive = generatePositiveWordNotExpression(word);
		Set<int[]> notNegative = generateNegativeWordNotExpression(word);

		if (notPositive == null || notNegative == null)
		{
			return null;
		}

		notPositive.addAll(notNegative);

		SynsetsPolarityStore.getInstance().cleanConflictSynsetsFor(
				word.getWord());
		return notPositive;
	}

	@Override
	protected Set<int[]> generatePositiveWordNotExpression(Word word)
	{
		return generateWordNotExpression(word.getMajoritySynsets(),
				Polarity.POSITIVE, word.getWord());
	}

	@Override
	protected Set<int[]> generateNegativeWordNotExpression(Word word)
	{
		return generateWordNotExpression(word.getMajoritySynsets(),
				Polarity.NEGATIVE, word.getWord());
	}

	/**
	 * C1 AND C2 AND C3 AND ...
	 * 
	 * If ANY of the clauses(Ci) is FALSE(int[0]), then the result will be FALSE, return null
	 * If ALL of the clauses(Ci) are TRUE(null), then the result will be TRUE, return empty set
	 */
	private Set<int[]> generateWordNotExpression(
			List<SynsetSet> majoritySynsets, Polarity polarityToGenerate,
			String word)
	{
		TreeSet<int[]> cnfs = new TreeSet<int[]>(new IntArrayComparator());

		for (int index = 0; index < majoritySynsets.size(); index++)
		{
			int[] clause = convertToCnfOrNotClause(majoritySynsets.get(index),
					polarityToGenerate, word);

			// the clause(Ci) is TRUE(null)
			if (clause == null)
			{
				continue;
			}
			// The word's formula is evaluated as FALSE
			else if (clause.length == 0)
			{
				return null;
			}

			cnfs.add(clause);
		}

		return cnfs;
	}

	/**
	 * (NOT v11) OR (NOT v21) OR (NOT v31) OR (NOT ...
	 * 
	 * If ANY of the (NOT v) is TRUE, v is FALSE, then the result will be TRUE, return null
	 * If ALL of the (NOT v) are FALSE, v is TRUE, then the result will be FALSE, return empty set
	 */
	private int[] convertToCnfOrNotClause(Set<Synset> onePossibleCombination,
			Polarity polarityToGenerate, String word)
	{
		ArrayIntList intList = new ArrayIntList(onePossibleCombination.size());
		boolean returnNull = false;
		for (Synset synset : onePossibleCombination)
		{
			Polarity polarityKnownInQWordNet = SynsetsPolarityStore
					.getInstance().getPolarity(synset.getId());

			// do not have polarity info.
			if (polarityKnownInQWordNet == null)
			{
				// here is the difference - NOT
				int symbol = not(getSymbolForPolarSynset(polarityToGenerate,
						synset));
				intList.add(symbol);
			}
			// ANY of the symbol is known as FALSE
			else if (isSymbolRepresentFalse(polarityKnownInQWordNet,
					polarityToGenerate))
			{
				// IMPORTANT record these false!
				returnNull = true;
				SynsetsPolarityStore.getInstance().recordConflictSynset(word,
						synset);
				//return null;
			}
			// the symbol are known as TRUE, do nothing...
		}

		if (returnNull)
		{
			return null;
		}

		int[] result = intList.toArray();
		Arrays.sort(result);

		return result;
	}

	/**
	 * If the result will be FALSE, return null<br>
	 * IF the result will be TRUE, return empty set
	 */
	private List<int[]> generateWordExpression(List<SynsetSet> majoritySynsets,
			Polarity polarity, String word)
	{
		/**
		 * This dnfList represent formula like this:
		 * C1 OR C2 OR C3 OR ...
		 * 
		 * If ANY of the clauses(Ci) is TRUE(int[0]), then the result will be TRUE, return empty set
		 * If ALL of the clauses(Ci) are FALSE(null), then the result will be FALSE, return null
		 * 	hence final result will be FALSE
		 */
		List<int[]> dnfList = new ArrayList<int[]>(majoritySynsets.size());
		for (int index = 0; index < majoritySynsets.size(); index++)
		{
			int[] clause = convertToDnfAndClause(majoritySynsets.get(index),
					polarity, word);
			// the clause(Ci) is FALSE(null)
			if (clause == null)
			{
				continue;
			}
			// The word's formula is evaluated as TRUE
			else if (clause.length == 0)
			{
				SynsetsPolarityStore.getInstance()
						.cleanConflictSynsetsFor(word);
				return Collections.emptyList();
			}

			dnfList.add(clause);
		}

		// ALL of the clauses(Ci) are FALSE(null)
		if (dnfList.isEmpty())
		{
			return null;
		}
		else
		{
			SynsetsPolarityStore.getInstance().cleanConflictSynsetsFor(word);
			return convertDnfToCnf(dnfList);
		}
	}

	/**
	 * The return value looks like:
	 * s1 AND s2 AND s3 AND ... 
	 * 
	 * If ANY of the symbol is known as FALSE, then the result will be FALSE, return null
	 * If ALL of the symbol are known as TRUE, then the result will be TRUE, return int[0]
	 */
	private int[] convertToDnfAndClause(Set<Synset> onePossibleCombination,
			Polarity polarityToGenerate, String word)
	{
		ArrayIntList intList = new ArrayIntList(onePossibleCombination.size());
		boolean returnNull = false;
		for (Synset synset : onePossibleCombination)
		{
			Polarity polarityKnownInQWordNet = SynsetsPolarityStore
					.getInstance().getPolarity(synset.getId());

			// do not have polarity info.
			if (polarityKnownInQWordNet == null)
			{
				int symbol = getSymbolForPolarSynset(polarityToGenerate, synset);
				intList.add(symbol);
			}
			// ANY of the symbol is known as FALSE
			else if (isSymbolRepresentFalse(polarityKnownInQWordNet,
					polarityToGenerate))
			{
				// IMPORTANT record these false!
				returnNull = true;
				SynsetsPolarityStore.getInstance().recordConflictSynset(word,
						synset);
				// return null;
			}
			// the symbol are known as TRUE, do nothing...
		}

		if (returnNull)
		{
			return null;
		}

		int[] result = intList.toArray();
		Arrays.sort(result);

		return result;
	}

	private boolean isSymbolRepresentTrue(Polarity polarityKnownInQWordNet,
			Polarity polarityToGenerate)
	{
		return polarityKnownInQWordNet.equals(polarityToGenerate);
	}

	private boolean isSymbolRepresentFalse(Polarity polarityKnownInQWordNet,
			Polarity polarityToGenerate)
	{
		return !isSymbolRepresentTrue(polarityKnownInQWordNet,
				polarityToGenerate);
	}

	private int getSymbolForPolarSynset(Polarity polarityToGenerate,
			Synset synset)
	{
		return polarSymbol(polarityToGenerate, indexOf(synset));

		/*switch (polarityToGenerate)
		{
			case POSITIVE:
				return positiveSymbol(indexOf(synset));
			case NEGATIVE:
				return negativeSymbol(indexOf(synset));
			default:
				// DO NOT have NEUTRAL symbols in clause other than basic terms
				throw new GeneralException("Can not be here! ");
		}*/
	}

}
